<?php
/**
 * @package qBase
 * @subpackage sql
 *
 */

/**
 * Represents an SQL abstract table
 * 
 * @package qBase
 * @subpackage sql
 */
class QSqlTable extends QObject implements QIAllowSerialize 
{
	/**
	 * The name of the table
	 *
	 * @var string
	 */
	public $name;
	/**
	 * The database that contains the table
	 *
	 * @var QSqlDatabase
	 */
	public $database;
	/**
	 * The fields of the table (array of QSqlTableField)
	 *
	 * @var array
	 */
	public $fields;
	/**
	 * The engine type for the table
	 *
	 * @var string
	 */
	public $engine;
	/**
	 * The collation of the table
	 *
	 * @var string
	 */
	public $collation;
	/**
	 * The charset of the table
	 *
	 * @var string
	 */
	public $charset;
	/**
	 * The indexes of the table (array of QSqlTableIndex)
	 *
	 * @var array
	 */
	public $indexes;
	/**
	 * The table comments
	 *
	 * @var string
	 */
	public $comments;
	
	/**
	 * The constructor for QSqlTable
	 *
	 * @param string $name
	 * @param array $fields
	 * @param array $indexes
	 * @param string $engine
	 * @param string $collation
	 * @param string $charset
	 */
	public function __construct(QSqlDatabase $database = null, $name = null, $fields = null, $indexes = null,
		$engine = null, $charset = null, $collation = null, $comments = null)
	{
		$this->database = $database;
		$this->name = $name;
		
		if ($fields)
		{
			foreach ($fields as $f)
				if (!$f)
					throw new Exception("A field can not be null");
			$this->fields = $fields;
		}
		
		$this->indexes = $indexes;
		$this->engine = $engine;
		$this->collation = $collation;
		$this->charset = $charset;
		$this->comments = $comments;
		
		if ($this->fields)
		{
			foreach ($this->fields as $field)
				$field->setTable($this);
		}
		if ($this->indexes)
		{
			foreach ($this->indexes as $index)
				$index->setTable($this);
		}
	}
	
	/**
	 * Gets the connection of the table
	 *
	 * @return QSqlConnection
	 */
	public function getConnection()
	{
		if ($this->database)
			return $this->database->getConnection();
		else 
			return null;
	}
	
	/**
	 * Ads a field to the table, the operation is executed on the database
	 *
	 * @param QSqlTableField $field
	 */
	public function addField(QSqlTableField $field)
	{
		$this->addFields(array($field));
	}
	
	/**
	 * Ads a list of fields to the table, the operation is executed on the database
	 *
	 * @param array $fields Array of QSqlTableField(s)
	 */
	public function addFields($fields)
	{
		$this->addOrReplaceFields($fields);
		if ($this->getConnection())
			$this->getConnection()->addTableFields($this->database->getName(), $this->getName(), $fields);
	}
	
	/**
	 * Ads or replaces (if the same name) fields from the table. The operation is NOT executed on the database.
	 *
	 * @param array $fields Array of QSqlTableField(s)
	 */
	private function addOrReplaceFields($fields)
	{
		$count = count($this->fields);
		if ($count > 0)
		{
			foreach ($fields as $f)
			{
				$found = false;
				for ($i = 0; $i < $count; $i++)
					if ($this->fields[$i]->getName() == $f->getName())
					{
						$found = true;
						$f->setTable($this);
						$this->fields[$i] = $f;
						break;
					}
				if (!$found)
				{
					$f->setTable($this);
					$this->fields[] = $f;
				}
			}
		}
		else 
		{
			foreach ($fields as $f)
			{
				$f->setTable($this);
				$this->fields[] = $f;
			}
		}
	}
	
	/**
	 * Updates a field in the table, the operation is executed on the database
	 *
	 * @param QSqlTableField $field
	 */
	public function updateField(QSqlTableField $field)
	{
		$this->updateFields(array($field));
	}
	
	/**
	 * Updates a list of fields in the table, the operation is executed on the database
	 *
	 * @param array $fields Array of QSqlTableField(s)
	 */
	public function updateFields($fields)
	{
		if ($this->getConnection())
			$this->getConnection()->updateTableFields($this->database->getName(), $this->getName(), $fields);
		foreach ($fields as $field)
			$field->renameDone();
	}
	
	/**
	 * Removes a field from the table, the operation is executed on the database
	 *
	 * @param string $field_name
	 */
	public function removeField($field_name)
	{
		$this->removeFields(array($field_name));
	}
	
	/**
	 * Removes a list of fields from the table, the operation is executed on the database
	 *
	 * @param array $fields_names Array of strings
	 */
	public function removeFields($fields_names)
	{
		if ($this->getConnection())
			$this->getConnection()->removeTableFields($this->database->getName(), $this->getName(), $fields_names);
			
		foreach ($fields_names as $field_name)
		{
			$field = $this->getField($field_name);
			if ($field)
			{
				$count = count($this->fields);
				for ($i = 0; $i < $count; $i++)
					if ($this->fields[$i]->getName() == $field->getName())
					{
						$this->fields[$i]->unsetTable();
						array_splice($this->fields, $i, 1);
						break;
					}
			}
		}
	}
	
	/**
	 * Ads an index to the table, the operation is executed on the database
	 *
	 * @param QSqlTableIndex $index
	 */
	public function addIndex(QSqlTableIndex $index)
	{
		if ($this->getConnection())
			$this->getConnection()->addTableIndex($this->database->getName(), $this->getName(), $index);
		$this->addOrReplaceIndex($index);
	}
	
	/**
	 * Ads or replaces (if the same name) an index in the list. The operation will NOT be executed on the database.
	 *
	 * @param QSqlTableIndex $index
	 */
	private function addOrReplaceIndex($index)
	{
		$count = count($this->indexes);
		
		if ($count > 0)
		{
			$found = 0;
			for ($i = 0; $i < $count; $i++)
				if ($this->indexes[$i]->getName() == $index->getName())
				{
					$found = true;
					$index->setTable($this);
					$this->indexes[$i] = $index;
					break;
				}
			if (!$found)
			{
				$index->setTable($this);
				$this->indexes[] = $index;
			}
		}
		else 
		{
			$index->setTable($this);
			$this->indexes[] = $index;
		}
	}
	
	/**
	 * Updates an index, the operation is executed on the database
	 *
	 * @param QSqlTableIndex $index
	 */
	public function updateIndex(QSqlTableIndex $index)
	{
		if ($this->getConnection())
			$this->getConnection()->updateTableIndex($this->database->getName(), $this->getName(), $index);
		$index->renameDone();
	}
	
	/**
	 * Removes an index, the operation is executed on the database
	 *
	 * @param string $index_name
	 */
	public function removeIndex($index_name)
	{
		if ($this->getConnection())
			$this->getConnection()->removeTableIndex($this->database->getName(), $this->getName(), $index_name);
			
		$count = count($this->indexes);
		
		for ($i = 0; $i < $count; $i++)
			if ($this->indexes[$i]->getName() == $index_name)
			{
				$this->indexes[$i]->unsetTable();
				array_splice($this->indexes, $i, 1);
				break;
			}
	}
	
	/**
	 * Renames this table, the operation is executed on the database
	 *
	 * @param string $new_name
	 */
	public function rename($new_name)
	{
		// apply changes on spot
		if ($this->getConnection())
			$this->getConnection()->renameTable($this->database->getName(), $this->getName(), $new_name);
	}
	
	/**
	 * Gets the name of the table
	 *
	 * @return string
	 */
	public function getName()
	{
		return $this->name;
	}
	
	/**
	 * Gets the database of the table
	 *
	 * @return QSqlDatabase
	 */
	public function getDatabase()
	{
		return $this->database;
	}
	
	/**
	 * Sets the database of the table
	 *
	 * @param QSqlDatabase $database
	 */
	public function setDatabase(QSqlDatabase $database)
	{
		$this->database = $database;
	}
	
	/**
	 * Unsets the database pointer
	 *
	 */
	public function unsetDatabase()
	{
		unset($this->database);
	}
	
	/**
	 * Gets the fields of the table.
	 *
	 * @return array Array of QSqlTableField
	 */
	public function getFields()
	{
		return $this->fields;
	}
	
	/**
	 * Gets the number of fields
	 *
	 * @return integer
	 */
	public function countFields()
	{
		if ($this->fields)
			return count($this->fields);
		else 
			return 0;
	}
	
	/**
	 * Gets the field with the specified name
	 *
	 * @param string $name
	 * @return QSqlTableField
	 */
	public function getField($name)
	{
		if ($this->fields)
		{
			foreach ($this->fields as $field)
				if ($field->getName() == $name)
					return $field;
		}
		return null;
	}
	
	/**
	 * Gets the field at the specified position or null if none was found
	 *
	 * @param integer $position
	 * @return QSqlTableField
	 */
	public function getFieldAt($position)
	{
		if ($this->fields)
		{
			if ($position < count($this->fields))
				return $this->fields[$position];
		}
		return null;
	}
	
	/**
	 * Gets the number of table indexes
	 *
	 * @return integer
	 */
	public function countIndexes()
	{
		if ($this->indexes)
			return count($this->indexes);
		else 
			return 0;
	}
	
	/**
	 * Gets the table index with the specified name
	 *
	 * @param string $name
	 * @return QSqlTableIndex
	 */
	public function getIndex($name)
	{
		if ($this->indexes)
		{
			foreach ($this->indexes as $index)
				if ($index->getName() == $name)
					return $index;
		}
		return null;
	}
	
	/**
	 * Gets the indexes of the table
	 *
	 * @return array Array of QSqlTableIndex
	 */
	public function getIndexes()
	{
		return $this->indexes;
	}
	
	/**
	 * Gets the table index at the specified position
	 *
	 * @param integer $position
	 * @return QSqlTableIndex
	 */
	public function getIndexAt($position)
	{
		if ($this->indexes)
		{
			if ($position < count($this->indexes))
				return $this->indexes[$position];
		}
		return null;
	}
	
	/**
	 * Renames the specified field name
	 *
	 * @param string $old_name
	 * @param string $new_name
	 */
	public function renameField($old_name, $new_name)
	{
		$field = $this->getField($old_name);
		if (!$field)
			throw new Exception("The field {$old_name} can not be found");
		$field->setName($new_name, true);
		$this->updateField($field);
	}
	
	/**
	 * Gets the engine of the table
	 *
	 * @return string
	 */
	public function getEngine()
	{
		return $this->engine;
	}
	
	/**
	 * Sets the engine of the table
	 *
	 * @param string $engine
	 */
	public function setEngine($engine)
	{
		if ($this->getConnection())
			$this->getConnection()->setTableEngine($this->database->getName(), $this->getName(), $engine);
		$this->engine = $engine;
	}
	
	/**
	 * Gets the charset of the table
	 *
	 * @return string
	 */
	public function getCharset()
	{
		return $this->charset;
	}
	
	/**
	 * Sets the charset of the table
	 *
	 * @param string $charset
	 */
	public function setCharset($charset)
	{
		if ($this->getConnection())
			$this->getConnection()->setTableCharset($this->database->getName(), $this->getName(), $charset);
		// set it in the database first !
		$this->charset = $charset;
	}
	
	/**
	 * Gets the collation of the table
	 *
	 * @return string
	 */
	public function getCollation()
	{
		return $this->collation;
	}
	
	/**
	 * Sets the collation of the table
	 *
	 * @param string $collation
	 */
	public function setCollation($collation)
	{
		if ($this->getConnection())
			$this->getConnection()->setTableCollation($this->database->getName(), $this->getName(), $collation);
		// set it in the database first !
		$this->collation = $collation;
	}
	
	/**
	 * Gets the comments for this table
	 *
	 * @return string
	 */
	public function getComments()
	{
		return $this->comments;
	}
	
	/**
	 * Sets the comments for this table
	 *
	 * @param string $comments
	 */
	public function setComments($comments)
	{
		if ($this->getConnection())
			$this->getConnection()->setTableComment($this->database->getName(), $this->getName(), $comments);
		// set it in the database first !
		$this->comments = $comments;
	}
	
	/**
	 * Creates a table and adds it to the database if a connection is linked to this object
	 *
	 * @param boolean $drop_if_exists
	 */
	public function create($drop_if_exists = false)
	{
		$this->database->createTable($this, $drop_if_exists);
	}
}

?>